/**
 * 
 */
package org.dragonfire.dasm.instr;

import java.util.ArrayList;

import org.dragonfire.dasm.Command;
import org.dragonfire.dasm.CompileException;
import org.dragonfire.dasm.DASM;
import org.dragonfire.dasm.Listing;
import org.dragonfire.util.DMath;
import org.dragonfire.util.DString;

/**
 * @author Darth Android
 * @date Dec 1, 2006
 * @version 2.0
 */
public abstract class CommandDef implements Comparable<CommandDef> {

	public static final String SORT_ORDER = "OLRPMSNV";
	private String name = "";
	private String[] params = null;
	private DASM compiler;

	public CommandDef(String name, String[] params) {
		if (name == null)
			throw new IllegalArgumentException("Name may not be null");
		if (params == null)
			params = new String[0];
		this.name = name.toLowerCase();
		this.params = params;
		this.params = validateParamTypes(getParamFormats());
		for (int i = 0; i < params.length; i++) {
			if (params[i] == null)
				throw new IllegalArgumentException("No parameters may be null (the " + (i + 1) + DMath.ordinal(i + 1)
						+ " one was.");
			params[i] = params[i].toUpperCase();
		}
	}

	public void setCompiler(DASM comp) {
		compiler = comp;
	}

	public DASM getCompiler() {
		return compiler;
	}

	/**
	 * Returns the name, or mnemonic, of this <code>Command</code>
	 * 
	 * @return
	 */
	public String getName() {
		return name;
	}

	public String[] getParamFormats() {
		return params;
	}

	public abstract ArrayList<Listing> pass1(Command c);

	/**
	 * @param post
	 *            A reference to the current postprocessor
	 * @param list
	 *            The listing on which to perform a second pass.
	 */
	public abstract void pass2(Listing list, Command c);

	public int compareTo(CommandDef other) {
		if (getParamFormats().length != other.getParamFormats().length)
			return getParamFormats().length - other.getParamFormats().length;
		String myParam = sortParams(getParamFormats());
		String othParam = sortParams(other.getParamFormats());
		for (int i = 0; i < myParam.length(); i++) {
			int diff = SORT_ORDER.indexOf(myParam.charAt(i)) - SORT_ORDER.indexOf(othParam.charAt(i));
			if (diff != 0)
				return diff;
		}
		return 0;
	}

	public static String[] validateParamTypes(String[] paramTypes) {
		if (paramTypes == null)
			paramTypes = new String[0];
		String[] out = new String[paramTypes.length];
		for (int i = 0; i < paramTypes.length; i++) {
			out[i] = validateParamType(paramTypes[i]);
		}
		return out;
	}

	public static String validateParamType(String paramType) {
		if (paramType == null)
			throw new IllegalArgumentException("paramType may not be null!");
		paramType = DString.trim(paramType);
		if (!paramType.contains(":")) {
			if (paramType.matches(DASM.LITERAL_REGEX))
				return "L:" + paramType;
			if (paramType.matches(DASM.OFFSET_REGEX)) {
				return "O:" + paramType;
			}
			return "V:" + paramType;
		}
		String[] parts = DString.splitTopLevel(paramType, ':');
		parts = DString.trim(parts);
		if (parts.length < 1)
			throw new CompileException("ERROR_PARAM_TYPE_MALFORMED", paramType);
		char type = parts[0].charAt(0);
		if (parts.length < 2)
			throw new CompileException("ERROR_PARAM_TYPE_TOO_SHORT", type + "", 2);
		try {
			switch (type) {
				case 'O':
					if (parts[1].matches(DASM.OFFSET_REGEX))
						return "O:" + parts[1];
					if (parts.length < 3)
						throw new CompileException("ERROR_PARAM_TYPE_TOO_SHORT", type + "", 3);
					int valSize = new Integer(parts[1]);
					int rClass = new Integer(parts[2]);
					paramType = "O:" + valSize + ":" + rClass;
					break;
				case 'V':
					try {
						int size = new Integer(parts[1]);
						paramType = "V:" + size;
					} catch (NumberFormatException ne) {
						paramType = "V:" + parts[1];
					}
					break;
				case 'M':
					int addrSize = new Integer(parts[1]);
					paramType = "M:" + addrSize;
					break;
				case 'P':
					if (parts.length < 3)
						throw new CompileException("ERROR_PARAM_TYPE_TOO_SHORT", type + "", 3);
					int ptrSize = new Integer(parts[1]);
					int ptrClass = new Integer(parts[2]);
					paramType = "P:" + ptrSize + ":" + ptrClass;
					break;
				case 'R':
					if (parts.length < 3)
						throw new CompileException("ERROR_PARAM_TYPE_TOO_SHORT", type + "", 3);
					int size = new Integer(parts[1]);
					int classID = new Integer(parts[2]);
					paramType = "R:" + size + ":" + classID;
					break;
				case 'L':
					String text = parts[1].toLowerCase();
					paramType = "L:" + text;
					break;
				case 'S':
					if (!parts[1].matches(DASM.WORD_REGEX))
						throw new CompileException("ERROR_VAR_REQUIRED", parts[1]);
					paramType = "S:" + parts[1];
					break;
				default:
					throw new CompileException("ERROR_INVALID_PARAM_TYPE", type + "");
			}
		} catch (NumberFormatException ne) {
			throw new CompileException("ERROR_INVALID_PARAM_TYPE_SIZE", type + "", parts[1]);
		}
		return paramType;
	}

	public String toString() {
		String output = getName().toUpperCase();
		if (getParamFormats().length > 0) {
			output += " {";
			for (int i = 0; i < getParamFormats().length; i++) {
				output += getParamFormats()[i];
				if (i + 1 < getParamFormats().length)
					output += ", ";
			}
			output += "}";
		}
		return output;
	}

	private String sortParams(String[] params) {
		String output = "";
		for (char c : SORT_ORDER.toCharArray()) {
			for (String param : params) {
				if (param.charAt(0) == c)
					output += c;
			}
		}
		return output;
	}
}
